package com.googlecode.gaal.suffix.impl;

import com.googlecode.gaal.data.api.IntSequence;
import com.googlecode.gaal.data.impl.ArraySequence;
import com.googlecode.gaal.suffix.algorithm.api.LcpTableBuilder;
import com.googlecode.gaal.suffix.algorithm.api.SuffixTableBuilder;
import com.googlecode.gaal.suffix.api.IntervalTree;
import com.googlecode.gaal.suffix.api.IntervalTree.Interval;;

public abstract class AbstractIntervalTree<T extends Interval> extends SuffixArrayBase implements IntervalTree<T> {

    public AbstractIntervalTree(IntSequence sequence, int alphabetSize, int[] suffixTable, int[] lcpTable) {
        super(sequence, alphabetSize, suffixTable, lcpTable);
    }

    public AbstractIntervalTree(IntSequence sequence, int alphabetSize, SuffixTableBuilder suffixTableBuilder,
            LcpTableBuilder lcpTableBuilder) {
        super(sequence, alphabetSize, suffixTableBuilder, lcpTableBuilder);
    }

    protected abstract class AbstractInterval implements Interval {
        protected final int left;
        protected final int right;

        protected AbstractInterval(int left, int right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public int left() {
            return left;
        }

        @Override
        public int right() {
            return right;
        }

        @Override
        public int size() {
            return right - left + 1;
        }

        @Override
        public IntSequence indices() {
            return new ArraySequence(suffixTable, left, right + 1);
        }

        @Override
        public IntSequence label() {
            return label(0);
        }

        @Override
        public IntSequence edgeLabel(Interval parent) {
            return label(parent.lcp());
        }

        private IntSequence label(int offset) {
            int start = suffixTable[left];
            if (isTerminal())
                return sequence.subSequence(start + offset, sequence.size());
            else
                return sequence.subSequence(start + offset, start + lcp());
        }

        @Override
        public boolean isTerminal() {
            return right == left;
        }

        @Override
        public String toString() {
            return String.format("%d-%d:%d", left, right, lcp());
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + left;
            result = prime * result + right;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            @SuppressWarnings("unchecked")
            AbstractInterval other = (AbstractInterval) obj;
            if (lcp() != other.lcp())
                return false;
            if (left != other.left)
                return false;
            if (right != other.right)
                return false;
            return true;
        }
        
    }
}
